package com.bifang.common.config.set;

import cn.hutool.core.date.DateUtil;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.deser.std.DateDeserializers;
import com.fasterxml.jackson.databind.module.SimpleModule;
import java.lang.reflect.Field;
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;
import java.util.stream.Collectors;
import lombok.SneakyThrows;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.ResourceHttpMessageConverter;
import org.springframework.http.converter.ResourceRegionHttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter;
import org.springframework.http.converter.xml.SourceHttpMessageConverter;
import org.springframework.util.ObjectUtils;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.spring.web.plugins.WebFluxRequestHandlerProvider;
import springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

/**
 * @description:
 * @author: chenzh
 * @time: 2020/4/19 19:40
 */
@Configuration
@EnableSwagger2
public class SwaggerConfig extends WebMvcConfigurationSupport {
    /** 业务接口 */
    @Bean
    public Docket createBusinessApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.jwsoft.manager"))
                .paths(PathSelectors.any())
                .build()
                .groupName("业务接口");
    }
    /** 系统通用 */
    @Bean
    public Docket createCoreRestApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.bifang.core"))
                .paths(PathSelectors.any())
                .build()
                .groupName("系统通用");
    }
    /** 登录认证 */
    @Bean
    public Docket createSSORestApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.bifang.sso"))
                .paths(PathSelectors.any())
                .build()
                .groupName("系统管理");
    }
    /** * */
    @Bean
    public Docket createFileRestApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.bifang.file"))
                .paths(PathSelectors.any())
                .build()
                .groupName("文件服务");
    }
    /** app移动端服务 */
    @Bean
    public Docket createAppApiRestApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.bifang.app.api"))
                .paths(PathSelectors.any())
                .build()
                .groupName("app移动端服务");
    }
    /** app管理系统服务 */
    @Bean
    public Docket createAppManagerRestApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.bifang.app.manager"))
                .paths(PathSelectors.any())
                .build()
                .groupName("app管理系统服务");
    }

    @Bean
    public Docket createWorkflowRestApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.bifang.workflow"))
                .paths(PathSelectors.any())
                .build()
                .groupName("工作流程接口");
    }
    /**
     * api信息
     *
     * @return
     */
    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("接口管理系统")
                .description("接口文档")
                .contact(new Contact("chenzh", "", "845078693@qq.com"))
                .version("1.0")
                .build();
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
        registry.addResourceHandler("swagger-ui.html")
                .addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("doc.html")
                .addResourceLocations("classpath:/META-INF/resources/");

        registry.addResourceHandler("/webjars/**")
                .addResourceLocations("classpath:/META-INF/resources/webjars/");
    }

    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.clear();
        StringHttpMessageConverter stringHttpMessageConverter =
                new StringHttpMessageConverter(Charset.forName("UTF-8"));
        converters.add(stringHttpMessageConverter);
        converters.add(new ByteArrayHttpMessageConverter());
        converters.add(new ResourceHttpMessageConverter());
        converters.add(new ResourceRegionHttpMessageConverter());
        try {
            converters.add(new SourceHttpMessageConverter<>());
        } catch (Throwable ex) {
            // Ignore when no TransformerFactory implementation is available...
        }
        converters.add(new AllEncompassingFormHttpMessageConverter());

        MappingJackson2HttpMessageConverter jackson2HttpMessageConverter =
                new MappingJackson2HttpMessageConverter();
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        objectMapper.configure(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL, true);
        objectMapper.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true);
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
        objectMapper.setTimeZone(TimeZone.getTimeZone("GMT+8"));
        objectMapper.setLocale(Locale.CHINA);
        // 设置序列化反序列化采⽤直接处理字段的⽅式，不依赖setter 和 getter
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE);
        objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);

        SimpleModule module = new SimpleModule();
        // ⾃定义反序列化：key-序列化字段类型,value-序列化所⽤对象,⽀持⾃定义及Jackson⾃带序列化器
        // 设置全局反序列化，string -> date
        module.addDeserializer(
                Date.class,
                new DateDeserializers.DateDeserializer() {
                    @SneakyThrows
                    @Override
                    public Date deserialize(JsonParser jsonParser, DeserializationContext ctxt) {
                        if (ObjectUtils.isEmpty(jsonParser.getText())) {
                            return null;
                        }
                        // 获取序列⽂本
                        String source = jsonParser.getText();
                        if (!ObjectUtils.isEmpty(source)) {
                            try {
                                String text =
                                        source.replace("-", "")
                                                .replace("/", "")
                                                .replace(" ", "")
                                                .replace(":", "");
                                if (text.matches("^\\d{4}\\d{1,2}$")) {
                                    return DateUtil.parse(text, "yyyyMM");
                                } else if (text.matches("^\\d{4}\\d{1,2}\\d{1,2}$")) {
                                    return DateUtil.parse(text, "yyyyMMdd");
                                } else if (text.matches(
                                        "^\\d{4}\\d{1,2}\\d{1,2}\\d{1,2}\\d{1,2}$")) {
                                    return DateUtil.parse(text, "yyyyMMddHHmm");
                                } else if (text.matches(
                                        "^\\d{4}\\d{1,2}\\d{1,2}\\d{1,2}\\d{1,2}\\d{1,2}$")) {
                                    return DateUtil.parse(text, "yyyyMMddHHmmss");
                                } else {
                                    throw new IllegalArgumentException(
                                            "Can't cast String to Date '" + source + "'");
                                }
                            } catch (Exception e) {
                                throw new IllegalArgumentException(
                                        "Can't cast String to Date '" + source + "'");
                            }
                        } else {
                            return null;
                        }
                    }
                });
        objectMapper.registerModule(module);

        List<MediaType> list = new ArrayList<MediaType>();
        list.add(MediaType.TEXT_HTML);
        list.add(MediaType.APPLICATION_JSON);
        list.add(MediaType.APPLICATION_FORM_URLENCODED);
        jackson2HttpMessageConverter.setSupportedMediaTypes(list);

        jackson2HttpMessageConverter.setDefaultCharset(Charset.forName("UTF-8"));
        jackson2HttpMessageConverter.setObjectMapper(objectMapper);
        converters.add(jackson2HttpMessageConverter);
    }

    /**
     * 跨域支持
     *
     * @param registry
     */
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOriginPatterns("*")
                .allowedHeaders("*")
                .allowedMethods("GET", "POST", "DELETE", "PUT")
                .allowCredentials(true)
                .allowedMethods("*")
                .maxAge(3600 * 24);
        super.addCorsMappings(registry);
    }

    @Bean
    public static BeanPostProcessor springfoxHandlerProviderBeanPostProcessor() {
        return new BeanPostProcessor() {

            @SneakyThrows
            @Override
            public Object postProcessAfterInitialization(Object bean, String beanName)
                    throws BeansException {
                if (bean instanceof WebMvcRequestHandlerProvider
                        || bean instanceof WebFluxRequestHandlerProvider) {
                    WebMvcRequestHandlerProvider bean1 = (WebMvcRequestHandlerProvider) (bean);
                    Field field = bean1.getClass().getDeclaredField("handlerMappings");
                    field.setAccessible(true);
                    Object handlerMappings = field.get(bean1);
                    customizeSpringfoxHandlerMappings(
                            (List<RequestMappingInfoHandlerMapping>) handlerMappings);
                }
                return bean;
            }

            private <T extends RequestMappingInfoHandlerMapping>
                    void customizeSpringfoxHandlerMappings(List<T> mappings) {
                List<T> copy =
                        mappings.stream()
                                .filter(mapping -> mapping.getPatternParser() == null)
                                .collect(Collectors.toList());
                mappings.clear();
                mappings.addAll(copy);
            }
        };
    }
}
